/**
 * 服务器相关
 *
 * Created by snowingsea on 2020/01/19.
 */
const { MongoClient } = require('mongodb');
const fs = require('fs-extra');
const path = require('path');
const debug = require('debug')('phoenix:server');
const mongoClient = require('./mongo/client');
// const redis = require('./redis');
const hydraClient = require('./hydra/thrift-client');
const amqp = require('./amqp');
const gitIgnoreGlobal = require('./git/gitignore_global');
const users = require('./users');
const utility = require('./utility');
const license = require('./../helpers/licenseHelper');
const rules = require('./rules');
const numbers = require('./numbers');
const environments = require('./environments');
const projects = require('./projects');
const projectVersions = require('./project-versions');
const { clearCache } = require('../cacheUtil');

/**
 * 初始化工具内置的编译环境信息
 * @param {Object} compilationData 编译器信息
 * @returns
 */
async function initEnvironments(compilationData) {
  debug('Waiting for init environment...');
  if (!compilationData) {
    return;
  }
  compilationData = JSON.parse(compilationData);
  const newEnvs = compilationData.systems;
  if (!newEnvs || Object.keys(newEnvs).length === 0) {
    return;
  }
  const sampleManager = await users.getUserByName(global.config.sampleManager.username);
  let envProject = (await projects.getProjects({}, { creatorId: sampleManager._id }))[0];
  const projectType = 'gcc';
  const language = 'c';
  if (!envProject) {
    envProject = await projects.addProject(
      sampleManager._id,
      '',
      language,
      projectType,
      null,
      null,
      sampleManager,
      null,
      true
    );
  }
  const originalEnvs = await environments.getEnvironments({}, { projectId: envProject._id });
  const originalEnvMap = new Map();
  originalEnvs.forEach((originalEnv) => {
    originalEnvMap.set(originalEnv.versionName, originalEnv);
  });
  const promises = [];
  Object.keys(newEnvs).forEach((envName) => {
    const newEnv = newEnvs[envName];
    const envObj = {
      version: newEnv.version,
      projectId: envProject._id,
      versionName: envName,
      projectType,
      projectTypes: newEnv['project types'] || {},
      macros: newEnv.macros,
      includes: newEnv.includes,
      arguments: newEnv.arguments,
      language: newEnv.language || language,
      builtIn: true,
      creatorId: sampleManager._id,
      description: newEnv.description || 'builtIn environment',
      systemUrl: newEnv.url,
      systems: newEnv.systems,
    };
    if (!originalEnvMap.has(envName)) {
      promises.push(() => projectVersions.addVersion(
        envObj.projectId,
        envObj,
        envProject,
        sampleManager,
        null,
        true
      ));
      return;
    }
    const originalEnv = originalEnvMap.get(envName);
    if (originalEnv.builtIn) {
      promises.push(() => projectVersions.updateVersion(
        originalEnvMap.get(envName)._id,
        envObj,
        originalEnvMap.get(envName),
        envProject,
        sampleManager
      ));
    } else {
      promises.push(() => projectVersions.updateVersion(
        originalEnv._id,
        { versionName: `${originalEnv.versionName}_1` },
        originalEnv,
        envProject,
        sampleManager
      ));
      promises.push(() => projectVersions.addVersion(
        envObj.projectId,
        envObj,
        envProject,
        sampleManager,
        null,
        true
      ));
    }
  });
  await utility.promiseChain(promises);
}
/**
 * 检查mongodb连接情况，并初始化数据
 * @return {Promise}
 */
function initMongodb(compilationData) {
  return new Promise((resolve, reject) => {
    const {
      host,
      port,
      admin,
      name,
      auth,
    } = global.config.storage.mongo;
    const { username, password } = admin;
    let url = `mongodb://${encodeURIComponent(username)}:${encodeURIComponent(password)}@${host}:${port}/admin`;
    if (utility.isDesktop()) {
      url = `mongodb://${host}:${port}/admin`;
    }

    let conn;
    MongoClient.connect(url, { useUnifiedTopology: true })
      .then((res) => {
        conn = res;
        return conn.db().admin().listDatabases();
      })
      .then((dbs) => {
        let dbExists = false;
        dbs.databases.forEach((db) => {
          if (db.name === name) {
            dbExists = true;
          }
        });
        if (dbExists) {
          return Promise.resolve();
        }
        const { username, password } = auth;
        const options = { roles: [{ role: 'readWrite', db: name }] };
        return conn.db(name).addUser(username, password, options).catch(() => { });
      })
      .then(() => conn.close())
      .then(() => debug('Waiting for mongo connection...'))
      .then(() => mongoClient.connect())
      .then(() => users.addSampleManager())
      .then(() => users.addSampleUsers())
      .then(() => users.resetAllUserStatus())
      .then(() => numbers.removeAllLocks())
      .then(() => initEnvironments(compilationData))
      .then(() => mongoClient.disconnect())
      .then(resolve)
      .catch(reject);
  });
}

/**
 * 检查redis连接情况，并初始化数据
 * @return {Promise}
 */
// function initRedis() {
//   return new Promise((resolve, reject) => {
//     debug('Waiting for redis connection...');
//     redis.connect()
//       .then(() => redis.disconnect())
//       .then(resolve)
//       .catch(reject);
//   });
// }

/**
 * 初始化核心层连接
 * @return {Promise}
 */
function initHydra() {
  return new Promise((resolve, reject) => {
    debug('Waiting for hydra connection...');
    hydraClient.connect()
      .then((compilationData) => hydraClient.disconnect().then(() => compilationData))
      .then(resolve)
      .catch(reject);
  });
}

/**
 * 初始化消息队列
 * @return {Promise}
 */
function initAMQP() {
  return new Promise((resolve, reject) => {
    debug('Waiting for amqp connection...');
    amqp.checkConnection()
      .then(resolve)
      .catch(reject);
  });
}

/**
 * 初始化git配置
 * @return {Promise}
 */
function initGit() {
  return new Promise((resolve, reject) => {
    const { gitDir, gitIgnoreGlobalName } = global.config.path;
    fs.ensureDir(gitDir)
      .then(() => utility.writeFile(gitIgnoreGlobal, path.join(gitDir, gitIgnoreGlobalName)))
      .then(resolve)
      .catch(reject);
  });
}

function scheduleSysUpdate() {
  return new Promise((res) => setTimeout(() => {
    license.checkExpiration()
      .then((valid) => {
        if (!valid) {
          return Promise.resolve();
        }
        return license.updateExpiration();
      })
      .then(res);
  }, 300000))
    .then(() => scheduleSysUpdate());
}

/**
 * 初始化
 * @return {Promise}
 */
function init() {
  return new Promise((resolve, reject) => {
    clearCache()
      .then(() => initHydra())
      .then((compilationData) => initMongodb(compilationData))
      .then(() => initAMQP())
      .then(() => initGit())
      .then(() => rules.initRules())
      .then(() => {
        scheduleSysUpdate();
      })
      .then(resolve)
      .catch(reject);
  });
}

/**
 * Export model definition object.
 */
module.exports = {
  init,
};
